package com.AdvancedDemos.单元测试_反射_注解.反射;

import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class 为什么要用反射 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException, IOException {

        /*
        通过使用反射将类型名称和方法名称字符串化,保存在配置文件中,大大降低了类与类之间的耦合性,使得代码更加通用,不必修改源代码,就可以创建不同的类,调用不同的方法
         */

        /**
         * 反射存在的意义:
         * 1.解耦合(耦合问题,两个类的关系过于紧密,导致修改一个类,另一个类也要修改) --- Cat类不再与本测试类挂狗,改变Cat类名,本测试类将不再需要修改任何代码,只需要修改config.properties配置文件中的键值即可
         * 2.增强代码扩展性 --- 配置文件中将className改为com.AdvancedDemos.单元测试_反射_注解_代理.反射.Dog即可实现调用Dog类中的eat()方法
         * 3.增强代码复用性 --- 将来className可以等于任何类名,methodName也可以是Animal中的任何方法
         *
         *
         * 反射的作用?
         * 基本作用:可以得到一个类的全部成分然后操作。
         * 可以破坏封装性。
         * 最重要的用途是:适合做Java的框架，基本上，主流的框架都会基于反射设计出一些通用的功能。
         */


//        Cat cat = new Cat();
//        cat.eat();//猫吃鱼

        //定义集合,用于读取配置文件中的值
        Properties properties = new Properties();
        FileReader fr = new FileReader("helloworld\\src\\com\\AdvancedDemos\\单元测试_反射_注解_代理\\反射\\config.properties");
        properties.load(fr);
        System.out.println(properties);//{methodName=eat, className=com.AdvancedDemos.单元测试_反射_注解_代理.反射.Dog}

        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");

        System.out.println(className);//com.AdvancedDemos.单元测试_反射_注解_代理.反射.Cat
        System.out.println(methodName);//eat

        Class clazz = Class.forName(className);
        Constructor constructor = clazz.getConstructor();//由于这个.java文件中定义了多个类,又只能有一个public修饰的类(文件同名的那个类),而Cat Dog类都是默认修饰符的,如果不显式地声明构造函数,那么无参构造默认会使用类的访问权限,即此处的默认修饰符,而clazz.getConstructor()只能拿到public修饰的构造函数,导致报错,解决办法是①使用clazz.getDeclaredConstructor()②用public修饰符显示地声明出无参构造
        Animal o = (Animal) constructor.newInstance();

        Method method = clazz.getDeclaredMethod(methodName);
        method.invoke(o);//猫吃鱼


    }
}

abstract class Animal {
    public abstract void eat();
}

class Cat extends Animal {
    public Cat() {
    }

    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

class Dog extends Animal {

    public Dog() {
    }

    @Override
    public void eat() {
        System.out.println("狗吃屎");
    }
}
